package exp.parser;

import error.processing.MyErrorProcessing;
import grammar.parser.FuncFParams;
import semantic.parser.SymbolTableElement;
import semantic.parser.VariableSymbol;
import word.parser.WordElement;

import java.util.ArrayList;
import java.util.List;

public class LVal extends ExpParser {
    private final String ident;
    private final List<Exp> offset;
    private final int dimension;


    public LVal(boolean isComputable, boolean isConst, String ident,
                List<Exp> offset, int dimension) {
        super(isComputable, isConst);
        this.ident = ident;
        this.offset = offset;
        this.dimension = dimension;
    }


    public static LVal buildLVal() {
        assert getAnalyzingWord().getType() == WordElement.WordType.IDENFR;
        String ident = getAnalyzingWord().getWord();
        VariableSymbol variableSymbol = getSemanticParser().getVariable(ident);
        boolean isComputable = false;
        boolean isConst = false;
        int dimension = 0;
        if (variableSymbol == null) {
            MyErrorProcessing.buildErrorProcessing(MyErrorProcessing.ErrorType.c, getAnalyzingWord());
        } else {
            dimension = variableSymbol.getDimension();
            if (variableSymbol.getType() == SymbolTableElement.SymbolType.CONST_INT) {
                isConst = true;
                isComputable = true;
            }
        }
        analyzeNextWord();
        List<Exp> offset = new ArrayList<>();
        while (getAnalyzingWord().getType() == WordElement.WordType.LBRACK) {
            analyzeNextWord();
            Exp exp = Exp.buildExp();
            isComputable = isComputable && exp.isComputable();
            offset.add(exp);
            if (getAnalyzingWord().getType() != WordElement.WordType.RBRACK) {
                MyErrorProcessing.buildErrorProcessing(MyErrorProcessing.ErrorType.k,
                        getAnalyzingWord(-1));
            } else {
                analyzeNextWord();
            }
        }
        dimension -= offset.size();
        return new LVal(isComputable, isConst, ident, offset, dimension);
    }

    @Override
    public LVal toLVal() {
        return this;
    }

    @Override
    public List<List<Integer>> getValueAsParam() {
        VariableSymbol variableSymbol = getSemanticParser().getVariable(ident);
        int valueDimension = variableSymbol.getDimension();
        List<List<Integer>> valInTable = variableSymbol.getValue();
        List<Integer> intOffset = new ArrayList<>();
        List<List<Integer>> ans = new ArrayList<>();
        int ansDimension = valueDimension - offset.size();
        for (Exp exp : offset) {
            intOffset.add(exp.getValue());
        }
        if (valueDimension == 2) {
            for (int i = intOffset.size(); i < 2; i++) {
                intOffset.add(0);
            }
        } else {
            for (int i = intOffset.size(); i < 2; i++) {
                intOffset.add(0, 0);
            }
        }
        // write by LXL
        switch (ansDimension) {
            case 0:
                ans.add(new ArrayList<>());
                ans.get(0).add(valInTable.get(intOffset.get(0)).get(intOffset.get(1)));
                break;

            case 1:
                ans.add(valInTable.get(intOffset.get(0)));
                break;

            case 2:
                ans = valInTable;
                break;
        }
        return ans;
    }

    @Override
    public Integer compute() {
        if (isComputable() && dimension == 0) {
            VariableSymbol variableSymbol = getSemanticParser().getVariable(ident);
            if (variableSymbol.getDimension() == 0) {
                return variableSymbol.getValue(0, 0);
            }
            List<Integer> offsetMatrix = new ArrayList<>();
            List<Integer> size = variableSymbol.getLenOfDimension();

//            if (this.offset.size() != size.size()) {
//                return null;
//            }

            for (Exp exp : this.offset) {
                offsetMatrix.add(exp.getValue());
            }
            for (int i = offsetMatrix.size(); i < 2; i++) {
                offsetMatrix.add(0, 0);
            }
            return variableSymbol.getValue(offsetMatrix.get(0), offsetMatrix.get(1));
        }
        return null;
    }

    @Override
    public FuncFParams.FuncFParam checkDimension() {
        return new FuncFParams.FuncFParam(dimension, 0);
    }

    public void assign(int val) {
        VariableSymbol variableSymbol = getSemanticParser().getVariable(ident);
        int y, x;
        if (variableSymbol.getDimension() == 2) {
            y = offset.get(0).getValue();
            x = offset.get(1).getValue();
        } else if (variableSymbol.getDimension() == 1) {
            y = 0;
            x = offset.get(0).getValue();
        } else {
            y = 0;
            x = 0;
        }
        variableSymbol.setValue(val, y, x);
    }
}
